/*
 * Copyright (C) 2021 The Chinese Software International Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.qiujuer.genius.ui.widget.popup;

import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.StackLayout;
import ohos.agp.components.element.StateElement;
import ohos.agp.utils.LayoutAlignment;
import ohos.agp.utils.Point;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayAttributes;
import ohos.agp.window.service.DisplayManager;
import ohos.agp.window.service.WindowManager;
import ohos.app.Context;
import ohos.media.image.common.PixelFormat;

import net.qiujuer.genius.ui.drawable.BalloonMarkerDrawable;
import net.qiujuer.genius.ui.widget.BalloonMarker;

import java.util.Optional;

/**
 * This is a SeekBar BalloonMarker PopupIndicator
 */
public class PopupIndicator {
    private final WindowManager mWindowManager;
    Point screenSize;
    private boolean mShowing;
    private Floater mPopupView;
    //Outside listener for the DiscreteSeekBar to get MarkerDrawable animation events.
    //The whole chain of events goes this way:
    //MarkerDrawable->Marker->Floater->mListener->DiscreteSeekBar....
    //... phew!
    private BalloonMarkerDrawable.MarkerAnimationListener mListener;

    private int[] mDrawingLocation = new int[2];

    public PopupIndicator(Context context) {
        mWindowManager = WindowManager.getInstance();
        System.out.println("==========PopupIndicator======>"+mWindowManager);
        mPopupView = new Floater(context);
        System.out.println("==========PopupIndicator======>"+mPopupView.getName());
        Optional<Display>
            display = DisplayManager.getInstance().getDefaultDisplay(context);
        DisplayAttributes displayAttributes = display.get().getAttributes();
        //screenSize.set(displayMetrics.widthPixels, displayMetrics.heightPixels);
        screenSize = new Point(displayAttributes.width, displayAttributes.height);


        System.out.println("==========PopupIndicator======>"+screenSize);
    }

    public PopupIndicator(Context context, int color, int textAppearanceId, float closeSize, String maxValue) {
        mWindowManager = WindowManager.getInstance();
        mPopupView = new Floater(context, color, textAppearanceId, closeSize, maxValue);
        Optional<Display>
            display = DisplayManager.getInstance().getDefaultDisplay(context);
        DisplayAttributes displayAttributes = display.get().getAttributes();
        //screenSize.set(displayMetrics.widthPixels, displayMetrics.heightPixels);
        screenSize = new Point(displayAttributes.width, displayAttributes.height);
    }

    public void setIndicatorColor(int color) {
        if (color == 0)
            return;
        dismissComplete();
        if (mPopupView != null) {
            mPopupView.mMarker.setBackgroundColor(color);
        }
    }

    public StateElement getIndicatorColor() {
        if (mPopupView != null) {
            return mPopupView.mMarker.getBackgroundColor();
        }
        return null;
    }

//    public void setTypeface(Typeface typeface) {
//        dismissComplete();
//        if (mPopupView != null) {
//            mPopupView.mMarker.setTypeface(typeface);
//        }
//    }

    public void setIndicatorSeparation(int separation) {
        dismissComplete();
        if (mPopupView != null) {
            mPopupView.mMarker.setSeparation(separation);
        }
    }

    public void setIndicatorTextPadding(int textPadding) {
        dismissComplete();
        if (mPopupView != null) {
            mPopupView.mMarker.setTextPadding(textPadding);
        }
    }

    public void setIndicatorTextAppearance(int textAppearanceId) {
        dismissComplete();
        if (mPopupView != null) {
            mPopupView.mMarker.setTextAppearance(textAppearanceId);
        }
    }

    public void setIndicatorClosedSize(float closeSize) {
        dismissComplete();
        if (mPopupView != null) {
            mPopupView.mMarker.setClosedSize(closeSize);
        }
    }

    public void setIndicatorSizes(String maxValue) {
        dismissComplete();
        if (mPopupView != null) {
            mPopupView.mMarker.resetSizes(maxValue);
        }
    }

    public void setListener(BalloonMarkerDrawable.MarkerAnimationListener listener) {
        System.out.println("==========PopupIndicator======01>");
        mListener = listener;
    }

    /**
     * We want the Floater to be full-width because the contents will be moved from side to side.
     * We may/should change this in the future to use just the PARENT View width and/or pass it in the constructor
     */
    private void measureFloater() {
        /*int specWidth = Component.MeasureSpec.getMeasureSpec(screenSize.getPointX(), Component.MeasureSpec.EXACTLY);
        int specHeight = Component.MeasureSpec.getMeasureSpec((screenSize.getPointY()), Component.MeasureSpec.AT_MOST);*/
        int specWidth = Component.EstimateSpec.getSizeWithMode((int) screenSize.getPointX(), Component.EstimateSpec.UNCONSTRAINT);
        int specHeight = Component.EstimateSpec.getSizeWithMode((int) screenSize.getPointY(), Component.EstimateSpec.UNCONSTRAINT);
        mPopupView.onEstimateSize(specWidth, specHeight);
    }

    public void setValue(CharSequence value) {
        mPopupView.mMarker.setValue(value);
    }

    public boolean isShowing() {
        return mShowing;
    }

    public void showIndicator(Component parent, Point point) {
        if (isShowing()) {
            mPopupView.mMarker.animateOpen();
            return;
        }

        // IBinder windowToken = parent.getWindowToken();
        //if (windowToken != null) {
        WindowManager.LayoutConfig p = createPopupLayout();

        /** Raw bit controlling whether the layout direction is relative or not (START/END instead of
         * absolute LEFT/RIGHT).
         */
        int RELATIVE_LAYOUT_DIRECTION = 0x00800000;

        /** Push object to x-axis position at the start of its container, not changing its size. */
        int START = RELATIVE_LAYOUT_DIRECTION | LayoutAlignment.LEFT;

        p.alignment = LayoutAlignment.TOP | START;
        updateLayoutParamsForPosition(parent, p, point.getPointYToInt());
        mShowing = true;

        translateViewIntoPosition(point.getPointXToInt());
        invokePopup(p);
        // }
    }

    public void move(int x) {
        if (!isShowing()) {
            return;
        }
        translateViewIntoPosition(x);
    }

    public void setColors(int startColor, int endColor) {
        mPopupView.setColors(startColor, endColor);
    }

    /**
     * This will start the closing animation of the Marker and call onClosingComplete when finished
     */
    public void dismiss() {
        mPopupView.mMarker.animateClose();
    }

    /**
     * FORCE the popup window to be removed.
     * You typically calls this when the parent view is being removed from the window to avoid a Window Leak
     */
    public void dismissComplete() {
        if (isShowing()) {
            mShowing = false;
//            try {
//                mWindowManager.removeViewImmediate(mPopupView);
//            } finally {
//                // Do...
//            }
        }
    }

    private void updateLayoutParamsForPosition(Component anchor, WindowManager.LayoutConfig p, int yOffset) {
        measureFloater();
        int measuredHeight = mPopupView.getEstimatedHeight();
        int paddingBottom = mPopupView.mMarker.getPaddingBottom();
        //anchor.getLocationInWindow(mDrawingLocation);
        mDrawingLocation = anchor.getLocationOnScreen();
        p.x = 0;
        p.y = mDrawingLocation[1] - measuredHeight + yOffset + paddingBottom;
        p.width = screenSize.getPointXToInt();
        p.height = measuredHeight;
    }

    private void translateViewIntoPosition(final int x) {
        mPopupView.setFloatOffset(x + mDrawingLocation[0]);
    }

    private void invokePopup(WindowManager.LayoutConfig p) {
        /*mWindowManager.addView(mPopupView, p);*/
        mWindowManager.addComponent(mPopupView, mPopupView.getContext(), 0);
        mPopupView.mMarker.animateOpen();
    }

    private WindowManager.LayoutConfig createPopupLayout() {
        WindowManager.LayoutConfig p = new WindowManager.LayoutConfig();
        p.alignment = LayoutAlignment.LEFT | LayoutAlignment.TOP;
        p.width = ComponentContainer.LayoutConfig.MATCH_PARENT;
        p.height = ComponentContainer.LayoutConfig.MATCH_PARENT;
        p.pixelFormat = PixelFormat.ARGB_8888.getValue();
        p.flags = computeFlags(p.flags);
        /*p.type = WindowManager.LayoutConfig.TYPE_APPLICATION_PANEL;*/
        p.type = WindowManager.LayoutConfig.MOD_APPLICATION_PANEL;
        //p.token = token;
        p.layoutInDisplaySideMode = WindowManager.LayoutConfig.INPUT_STATE_ALWAYS_HIDDEN;
        p.title.equals("DiscreteSeekBar Indicator:" + Integer.toHexString(hashCode()));

        return p;
    }

    /**
     * I'm NOT completely sure how all this bitwise things work...
     *
     * @param curFlags Cur Flags
     * @return Flags
     */
    private int computeFlags(int curFlags) {
        curFlags &= ~(
            WindowManager.LayoutConfig.MARK_NOT_RESPOD_CHEEK_PRESSES |
                WindowManager.LayoutConfig.MARK_ALT_FOCUSABLE_IM |
                WindowManager.LayoutConfig.MARK_TOUCHABLE_IMPOSSIBLE |
                WindowManager.LayoutConfig.MARK_WATCH_OUTSIDE_TOUCH |
                /*WindowManager.LayoutConfig.FLAG_LAYOUT_NO_LIMITS |*/
                WindowManager.LayoutConfig.ALTER_FLAGS |
                WindowManager.LayoutConfig.MARK_ALT_FOCUSABLE_IM);
        curFlags |= WindowManager.LayoutConfig.MARK_NOT_RESPOD_CHEEK_PRESSES;
        curFlags |= WindowManager.LayoutConfig.MARK_FOCUSABLE_IMPOSSIBLE;
        curFlags |= WindowManager.LayoutConfig.MARK_FOCUSABLE_IMPOSSIBLE;
        /*curFlags |= WindowManager.LayoutConfig.FLAG_LAYOUT_NO_LIMITS;*/
        curFlags |= WindowManager.LayoutConfig.ALTER_FLAGS;
        return curFlags;
    }

    /**
     * Small FrameLayout class to hold and move the bubble around when requested
     * I wanted to use the {@link BalloonMarker} directly
     * but doing so would make some things harder to implement
     * (like moving the marker around, having the Marker's outline to work, etc)
     */
    private class Floater extends StackLayout implements BalloonMarkerDrawable.MarkerAnimationListener, Component.EstimateSizeListener,
        ComponentContainer.ArrangeListener {
        private BalloonMarker mMarker;
        private int mOffset;

        public Floater(Context context) {
            super(context);
            mMarker = new BalloonMarker(context);
            System.out.println("==============mMarker============="+mMarker.toString());
            addComponent(mMarker, new LayoutConfig(ComponentContainer.LayoutConfig.MATCH_CONTENT, ComponentContainer.LayoutConfig.MATCH_PARENT, LayoutAlignment.LEFT | LayoutAlignment.TOP));
            setEstimateSizeListener(this);
        }

        public Floater(Context context, int color, int textAppearanceId, float closeSize, String maxValue) {
            this(context);
            mMarker.setBackgroundColor(color);
            mMarker.setTextAppearance(textAppearanceId);
            mMarker.setClosedSize(closeSize);
            mMarker.resetSizes(maxValue);
            setEstimateSizeListener(this);
        }

        @Override
        public boolean onEstimateSize(int widthMeasureSpec, int heightMeasureSpec) {
            int widthSize = EstimateSpec.getSize(widthMeasureSpec);
            int heightSie = mMarker.getEstimatedHeight();
            setEstimatedSize(widthSize, heightSie);
            return true;
        }

        @Override
        public boolean onArrange(int i, int i1, int i2, int i3) {
            int centerDiffX = mMarker.getEstimatedWidth() / 2;
            int offset = (mOffset - centerDiffX);
            mMarker.arrange(offset, 0, offset + mMarker.getEstimatedWidth(), mMarker.getEstimatedHeight());
            return false;
        }

        public void setFloatOffset(int x) {
            mOffset = x;
            int centerDiffX = mMarker.getEstimatedWidth() / 2;
            int offset = (x - centerDiffX);
            // mMarker.offsetLeftAndRight(offset - mMarker.getLeft());
            mMarker.setMarginsLeftAndRight(offset - mMarker.getLeft(), 0);
            //Without hardware acceleration (or API levels<11), offsetting a view seems to NOT invalidate the proper area.
            //We should calc the proper invalidate Rect but this will be for now...
            invalidate();

        }

        @Override
        public void onClosingComplete() {
            if (mListener != null) {
                mListener.onClosingComplete();
            }
            dismissComplete();
        }

        @Override
        public void onOpeningComplete() {
            if (mListener != null) {
                mListener.onOpeningComplete();
            }
        }

        public void setColors(int startColor, int endColor) {
            mMarker.setColors(startColor, endColor);
        }

    }
}
